import { createTaskQueue, arrified } from '../Misc';

// 任务队列
const taskQueue = createTaskQueue();
// 当前执行的任务
let subTask = null;

const reconcileChildren = (fiber, children) => {
  /**
   * 首先需要处理一下 children
   *    1. 在render中，children给的是一个对象
   *    2. 在 createElement 方法中，children给的是一个数组
   * 需要统一的先处理成数组
   */
  const arrifiedChildren = arrified(children);
  let index = 0;
  let numberOfElements = arrifiedChildren.length;
  let element = null;
  let newFiber = null; // 当前新的 fiber 对象
  let prevFiber = null; // 上一个 fiber 对象

  while (index < numberOfElements) {
    element = arrifiedChildren[index];
    newFiber = {
      type: element.type,
      props: element.props,
      tag: 'host_component',
      effects: [],
      effectTag: 'placement',
      stateNode: null,
      parent: fiber,
    };
    if (index === 0) {
      // 第一个节点才是子节点
      fiber.child = newFiber;
    } else {
      // 后面的节点都是上一个节点的兄弟节点
      prevFiber.sibling = newFiber;
    }
    prevFiber = newFiber;
    index++;
  }
};

// 获取第一个任务的方法
const getFirstTask = () => {
  const task = taskQueue.pop();
  // 构建最外层节点的 fiber 对象
  return {
    props: task.props,
    stateNode: task.dom,
    tag: 'host_root',
    effects: [],
    child: null,
  };
};

// 执行任务的方法，需要返回下一个任务
const executeTask = (fiber) => {
  // 先构建子节点，再构建兄弟节点
  reconcileChildren(fiber, fiber.props.children);
};

// 循环执行任务的方法
const workLoop = (deadline) => {
  // 获取 任务
  if (!subTask) subTask = getFirstTask();

  // 循环 执行任务
  while (subTask && deadline.timeRemaining() > 1) {
    subTask = executeTask(subTask);
  }
};

// 开始执行任务的方法
const performTask = (deadline) => {
  // 循环执行任务
  workLoop(deadline);

  // 还有任务就需要继续执行任务
  if (subTask || !taskQueue.isEmpty()) {
    requestIdleCallback(performTask);
  }
};

export const render = (element, dom) => {
  /**
   * 1. 向任务队列中添加任务
   *    1.1. 任务是指 通过 vdom 对象 构建 fiber 对象
   * 2. 指定在浏览器空闲的时候执行任务
   *    2.1. 任务队列就是一个存储任务的数组
   */

  // 1. 向任务队列中添加任务
  taskQueue.push({
    dom,
    props: { children: element },
  });

  // 2. 指定在浏览器空闲的时候执行任务
  requestIdleCallback(performTask);
};
